import { sys } from 'cc';
import { _decorator, color, director, System } from 'cc';
import { BaseManager } from '../../scripts/libs/rxjs/cc3/BaseManager';
import { native } from 'cc';
import { Observable } from '../libs/rxjs/internal/Observable';
import { NATIVE } from 'cc/env';
import { EMPTY, of } from 'rxjs';
const { ccclass, property } = _decorator;

@ccclass('HotUpdateManager')
export class HotUpdateManager extends BaseManager {

    public static instance: HotUpdateManager;
    static ID = 'hotupdate_system';
    get logColorOverride() { return color().fromHEX('#EECCDD') };

    static registerSystem() {
        director.unregisterSystem(director.getSystem(HotUpdateManager.ID));
        director.registerSystem(HotUpdateManager.ID, hum, System.Priority.MEDIUM);
        hum.init();
    }

    static unregisterSystem() {
        hum.destroy();
        director.unregisterSystem(director.getSystem(HotUpdateManager.ID));
    }

    onInitManager() {
        this.log('onInitManager', this.timestamp);
    }

    useObserves() {
        return [
        ];
    }

    getStoragePath(bundleName: string = null) {
        if (!bundleName) {
            return native.fileUtils.getWritablePath() + 'blackjack-remote-asset';
        } else {
            return native.fileUtils.getWritablePath() + `blackjack-remote-asset/subgames/assets/${bundleName}`;
        }
    }

    ams: Record<string, native.AssetsManager> = {};

    versionCompareHandle = function (versionA: string, versionB: string) {
        this.log("Compare: version A is " + versionA + ', version B is ' + versionB);
        var vA = versionA.split('.');
        var vB = versionB.split('.');
        for (var i = 0; i < vA.length; ++i) {
            var a = parseInt(vA[i]);
            var b = parseInt(vB[i] || '0');
            if (a === b) {
                continue;
            } else {
                return a - b;
            }
        }
        if (vB.length > vA.length) {
            return -1;
        } else {
            return 0;
        }
    };

    static StateToString(state: native.AssetsManager.State) {
        switch (state) {
            case native.AssetsManager.State.UNINITED: return 'UNINITED';
            case native.AssetsManager.State.UNCHECKED: return 'UNCHECKED';
            case native.AssetsManager.State.PREDOWNLOAD_VERSION: return 'PREDOWNLOAD_VERSION';
            case native.AssetsManager.State.DOWNLOADING_VERSION: return 'DOWNLOADING_VERSION';
            case native.AssetsManager.State.VERSION_LOADED: return 'VERSION_LOADED';
            case native.AssetsManager.State.PREDOWNLOAD_MANIFEST: return 'PREDOWNLOAD_MANIFEST';
            case native.AssetsManager.State.DOWNLOADING_MANIFEST: return 'DOWNLOADING_MANIFEST';
            case native.AssetsManager.State.MANIFEST_LOADED: return 'MANIFEST_LOADED';
            case native.AssetsManager.State.NEED_UPDATE: return 'NEED_UPDATE';
            case native.AssetsManager.State.READY_TO_UPDATE: return 'READY_TO_UPDATE';
            case native.AssetsManager.State.UPDATING: return 'UPDATING';
            case native.AssetsManager.State.UNZIPPING: return 'UNZIPPING';
            case native.AssetsManager.State.UP_TO_DATE: return 'UP_TO_DATE';
            case native.AssetsManager.State.FAIL_TO_UPDATE: return 'FAIL_TO_UPDATE';
            default: return '';
        }
    }

    static EventCodeToString(eventCode: number) {
        switch (eventCode) {
            case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: return 'ERROR_NO_LOCAL_MANIFEST';
            case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: return 'ERROR_DOWNLOAD_MANIFEST';
            case native.EventAssetsManager.ERROR_PARSE_MANIFEST: return 'ERROR_PARSE_MANIFEST';
            case native.EventAssetsManager.NEW_VERSION_FOUND: return 'NEW_VERSION_FOUND';
            case native.EventAssetsManager.ALREADY_UP_TO_DATE: return 'ALREADY_UP_TO_DATE';
            case native.EventAssetsManager.UPDATE_PROGRESSION: return 'UPDATE_PROGRESSION';
            case native.EventAssetsManager.ASSET_UPDATED: return 'ASSET_UPDATED';
            case native.EventAssetsManager.ERROR_UPDATING: return 'ERROR_UPDATING';
            case native.EventAssetsManager.UPDATE_FINISHED: return 'UPDATE_FINISHED';
            case native.EventAssetsManager.UPDATE_FAILED: return 'UPDATE_FAILED';
            case native.EventAssetsManager.ERROR_DECOMPRESS: return 'ERROR_DECOMPRESS';
            default: return 'UNKNOWN_EVENT';
        }
    }

    startProcess(bundleName: string = null, justCheck = false): Observable<{ event: native.EventAssetsManager }> {
        if (!NATIVE) return EMPTY;
        return new Observable(observer => {
            let manifestUrl = justCheck ? 'version.manifest' : 'project.manifest';
            if (!!bundleName) {
                manifestUrl = `${bundleName}_${manifestUrl}`;
            }
            let am = native.AssetsManager.create(manifestUrl, this.getStoragePath(bundleName));
            am.setVersionCompareHandle(this.versionCompareHandle.bind(this));
            am.setEventCallback((event: native.EventAssetsManager) => {
                let eventCode = event.getEventCode();
                let state = event.getAssetsManagerEx().getState();
                this.log('@event: @state', state, HotUpdateManager.StateToString(state));
                observer.next({ event });
                switch (eventCode) {
                    case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.ERROR_PARSE_MANIFEST:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.NEW_VERSION_FOUND:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.ALREADY_UP_TO_DATE:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        observer.complete();
                        break;
                    case native.EventAssetsManager.UPDATE_PROGRESSION:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode, event.getPercent(), event.getPercentByFile());
                        break;
                    case native.EventAssetsManager.ASSET_UPDATED:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.ERROR_UPDATING:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.UPDATE_FINISHED:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.UPDATE_FAILED:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    case native.EventAssetsManager.ERROR_DECOMPRESS:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                    default:
                        this.log('@event:', HotUpdateManager.EventCodeToString(eventCode), eventCode);
                        break;
                }
            });

            let state = am.getState();
            this.log('@state', state, HotUpdateManager.StateToString(state));

            if (justCheck) {
                am.checkUpdate();
            } else {
                am.update();
            }
        });
    }

}
globalThis.HotUpdateManager = HotUpdateManager;

export const hum = HotUpdateManager.instance = new HotUpdateManager();